package user

import (
	"clouddisk/internal/common"
	"clouddisk/model"
	pb "clouddisk/proto/user"
	"context"
	"fmt"
	"log"
	"net"
	"os"
	"os/signal"
	"syscall"
	"unicode"

	"google.golang.org/grpc"
	"gorm.io/gorm"
)

type UserServiceImpl struct {
	pb.UserServiceServer
	registry *common.Registry
	mysql    *gorm.DB
}

func IsValidUsername(username, password string) error {
	if len(username) < 4 || len(username) > 20 {
		return fmt.Errorf("用户名长度非法，请重新输入")
	}

	for _, c := range username {
		if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' {
			return fmt.Errorf("用户名含有非法字符，请重新输入")
		}
	}

	if len(password) < 6 || len(password) > 20 {
		return fmt.Errorf("密码长度非法，请重新输入")
	}

	return nil
}

func (us *UserServiceImpl) UserLogin(ctx context.Context, req *pb.UserLoginReq) (rsp *pb.UserLoginRsp, err error) {
	rsp = new(pb.UserLoginRsp)
	rsp.RequestId = req.RequestId
	err = IsValidUsername(req.Nickname, req.Password)
	if err != nil {
		rsp.Code = 1
		rsp.Errmsg = fmt.Sprint(err)
		return rsp, nil
	}

	// 用户鉴权
	var user model.UserInfo
	err = us.mysql.Model(&model.UserInfo{}).Where("nickname = ?", req.Nickname).First(&user).Error
	if err != nil {
		rsp.Code = 1
		return rsp, err
	}

	if user.Password != req.Password {
		rsp.Code = 1
		rsp.Errmsg = "账号或密码错误"
		return rsp, nil
	}

	// 组织返回
	rsp.Code = 2
	rsp.UserId = user.Uid
	return rsp, nil
}

func (us *UserServiceImpl) UserRegister(ctx context.Context, req *pb.UserRegisterReq) (rsp *pb.UserRegisterRsp, err error) {
	rsp = new(pb.UserRegisterRsp)
	rsp.RequestId = req.RequestId
	err = IsValidUsername(req.Nickname, req.Password)
	if err != nil {
		rsp.Code = 1
		rsp.Errmsg = fmt.Sprint(err)
		return rsp, nil
	}

	// 判断账号重复
	var exist bool
	err = us.mysql.Model(&model.UserInfo{}).Select("COUNT(*) > 0").Where("nickname = ?", req.Nickname).Find(&exist).Error
	if exist {
		rsp.Code = 1
		rsp.Errmsg = "该用户名已存在，请重试"
		return rsp, nil
	}

	// 存储用户信息
	ret := us.mysql.Create(&model.UserInfo{
		Uid:      common.GenerateRandomString(16),
		Nickname: req.Nickname,
		Password: req.Password,
	})
	if ret.Error != nil {
		rsp.Code = 1
		return rsp, fmt.Errorf("插入数据失败: %w", ret.Error)
	}

	rsp.Code = 2
	return rsp, nil
}

type UserServer struct {
	rpcServer *grpc.Server
	listener  net.Listener
	registry  *common.Registry
	mysql     *gorm.DB
}

func (fs *UserServer) Serve() error {
	stopChan := make(chan os.Signal, 1)
	signal.Notify(stopChan, os.Interrupt, syscall.SIGTERM)

	go func() {
		<-stopChan
		log.Println("程序开始平滑退出")
		fs.rpcServer.GracefulStop()
	}()

	if err := fs.rpcServer.Serve(fs.listener); err != nil {
		return fmt.Errorf("failed to serve: %v", err)
	}

	return nil
}

type UserServiceBuilder struct {
	rpcServer *grpc.Server
	db        *gorm.DB
	registry  *common.Registry
	listener  net.Listener
	err       error
}

func NewUsererverBuilder() *UserServiceBuilder {
	return &UserServiceBuilder{}
}

func (fb *UserServiceBuilder) WithMysqlObject(dsn string) *UserServiceBuilder {
	if fb.err != nil {
		return fb
	}
	fb.db, fb.err = common.ConnectMysql(dsn)
	if fb.err != nil {
		return fb
	}
	fb.err = fb.db.AutoMigrate(&model.UserInfo{}) // 自动创建表
	return fb
}

func (fb *UserServiceBuilder) WithRegisterObject(host, ServiceName, AccessHost string) *UserServiceBuilder {
	if fb.err != nil {
		return fb
	}
	fb.registry, fb.err = common.NewRegistry(host)
	if fb.err != nil {
		return fb
	}
	fb.err = fb.registry.Register(ServiceName, AccessHost)
	return fb
}

func (fb *UserServiceBuilder) WithRPCObject(port string) *UserServiceBuilder {
	if fb.err != nil {
		return fb
	}

	if fb.registry == nil || fb.db == nil {
		fb.err = fmt.Errorf("必须组件未完成初始化")
		return fb
	}

	fb.listener, fb.err = net.Listen("tcp", ":"+port)
	fb.rpcServer = grpc.NewServer()
	pb.RegisterUserServiceServer(fb.rpcServer, &UserServiceImpl{
		registry: fb.registry,
		mysql:    fb.db,
	})

	return fb
}

func (fb *UserServiceBuilder) Build() (*UserServer, error) {
	if fb.err != nil {
		return nil, fb.err
	}

	if fb.rpcServer == nil {
		fb.err = fmt.Errorf("rpcserver未完成初始化")
		return nil, fb.err
	}

	return &UserServer{
		rpcServer: fb.rpcServer,
		listener:  fb.listener,
	}, nil
}
